/*
    Author: Christopher Diggins
    License: MIT Licence 1.0
    Website: http://www.cdiggins.com
*/

#ifndef CVML_ABSTRACT_INTERPRETER_HPP
#define CVML_ABSTRACT_INTERPRETER_HPP

struct AbstractInterpreter
{
    TypeStack stk;
    std::vector<Subroutine*> subs;
    std::vector<Instruction*> rets;
    
	const TypeStack& GetStack() {
		return stk;
	}

	void Update(Instruction* i)
	{
		Type* tmp = NULL;

        switch (i->opcode)
        {
        case(op_noop) : 
            break;
        case(op_push_int) : 
            stk.Push(int_type);
            break;
        case(op_push_char) : 
            stk.Push(char_type);
            break;
        case(op_push_byte) : 
            stk.Push(int_type);
            break;
        case(op_push_sub) : 
            stk.Push(sub_type);
            break;
        case(op_push_neg1) : 
            stk.Push(int_type);
            break;
        case(op_push0) : 
            stk.Push(int_type);
            break;
        case(op_push1) : 
            stk.Push(int_type);
            break;
        case(op_push2) : 
            stk.Push(int_type);
            break;
        case(op_push_true) : 
            stk.Push(bool_type);
            break;
        case(op_push_false) : 
            stk.Push(bool_type);
            break;
        case(op_push_string) : 
            stk.Push(str_type);
            break;
        case(op_pop3) :
            stk.Pop();
            stk.Pop();
            stk.Pop();
            break;
        case(op_pop2) : 
            stk.Pop();
            stk.Pop();
            break;
        case(op_pop) : 
            stk.Pop();
            break;
        case(op_popd) :
            stk.Swap();
            stk.Pop();
            break;
        case (op_dup) :
            stk.Push(stk.Peek());
            break;
        case (op_dupd) :
            tmp = stk.Pull();
            stk.Push(stk.Peek());
            stk.Push(tmp);
            break;
        case (op_dup2) :
            stk.Push(stk.Peek());
            stk.Push(stk.Peek());
            break;
        case (op_dup3) :
            stk.Push(stk.Peek());
            stk.Push(stk.Peek());
            stk.Push(stk.Peek());
            break;
        case (op_swap) :
            stk.Swap();
            break;
        case (op_swapd) :
            tmp = stk.Pull();
            stk.Swap();
            stk.Push(tmp);
            break;
        case (op_swap1):
            tmp = stk.Top();
            stk.Top() = stk.Below(1);
            stk.Below(1) = tmp;
            break;
        case (op_swap2):
            tmp = stk.Top();
            stk.Top() = stk.Below(2);
            stk.Below(2) = tmp;
            break;
        case (op_swap3):
            tmp = stk.Top();
            stk.Top() = stk.Below(3);
            stk.Below(3) = tmp;
            break;
        case (op_swapN):
            tmp = stk.Top();
            stk.Top() = stk.Below(i->operand);
            stk.Below(i->operand) = tmp;
            break;
        case (op_getN):
            stk.Push(stk.Below(i->operand));
            break;
        case (op_get1):
            stk.Push(stk.Below(1));
            break;
        case (op_get2):
            stk.Push(stk.Below(2));
            break;
        case (op_get3):
            stk.Push(stk.Below(3));
            break;
        case (op_get4):
            stk.Push(stk.Below(4));
            break;
        case (op_get5):
            stk.Push(stk.Below(5));
            break;
        case (op_get6):
            stk.Push(stk.Below(6));
            break;
        case (op_get7):
            stk.Push(stk.Below(7));
            break;
        case (op_get8):
            stk.Push(stk.Below(8));
            break;
        case (op_setN):
            stk.Below(i->operand) = stk.Pull();
            break;
        case (op_set1):
            stk.Swap();
            stk.Pop();
            break;
        case (op_set2):
            stk.Below(1) = stk.Pull();
            break;
        case (op_set3):
            stk.Below(2) = stk.Pull();
            break;
        case (op_set4):
            stk.Below(3) = stk.Pull();
            break;
        case (op_set5):
            stk.Below(4) = stk.Pull();
            break;
        case (op_set6):
            stk.Below(5) = stk.Pull();
            break;
        case (op_set7):
            stk.Below(6) = stk.Pull();
            break;
        case (op_set8):
            stk.Below(7) = stk.Pull();
            break;
        case (op_lt_int):
        case (op_gt_int):
        case (op_lteq_int):
        case (op_gteq_int):
        case (op_eq_int):
        case (op_neq_int):
            stk.Pop(int_type);
            stk.Pop(int_type);
            stk.Push(bool_type);
            break;
        case (op_lt_flt):
        case (op_gt_flt):
        case (op_lteq_flt):
        case (op_gteq_flt):
        case (op_eq_flt):
        case (op_neq_flt):
            stk.Pop(flt_type);
            stk.Pop(flt_type);
            stk.Push(bool_type);
            break;
        case (op_lt):
        case (op_gt):
        case (op_lteq):
        case (op_gteq):
        case (op_eq):
        case (op_neq):
            stk.Pop();
            stk.Pop();
            stk.Push(bool_type);
            break;
        case (op_neg):
            break;
        case (op_neg_flt):
            stk.Peek(flt_type);
            break;
        case (op_neg_int):
        case (op_inc):
        case (op_dec):
            stk.Peek(int_type);
            break;
        case (op_add_int):
        case (op_sub_int):
        case (op_div_int):
        case (op_mul_int):
        case (op_mod_int):
            stk.Pop(int_type);
            stk.Peek(int_type);
            break;
        case (op_add_flt):
        case (op_sub_flt):
        case (op_mul_flt):
        case (op_div_flt):
            stk.Pop(flt_type);
            stk.Peek(flt_type);
            break;
        case (op_or):
        case (op_and):
        case (op_xor):
            stk.Pop(bool_type);
            stk.Peek(bool_type);
            break;
        case (op_not):
            stk.Peek(bool_type);
            break;
        case (op_call):
            // TODO: handle properly
			break;
        case (op_tail_call):
            // TODO: handle properly
            break;
        case (op_while):
            stk.Pop(sub_type);
            stk.Pop(sub_type);
            break;
        case (op_if):
            stk.Pop(sub_type);
            stk.Pop(sub_type);
            stk.Pop(bool_type);
            break;
        case (op_ret) :
			// TODO: handle properly
			break;

        // Unimplemented op-codes
        case (op_get_slot) :
        case (op_set_slot) :
        case (op_new) : 
        case (op_null) :
        case (op_new_array) :
        case (op_count):
        case (op_get_at):
        case (op_set_at):
        case (op_fold):
        case (op_map):
        case (op_filter):
        case (op_filter_map):
        case (op_range):
        case (op_empty):
        case (op_nil):
        case (op_dip):
        case (op_extended):
        case (op_quote):
        case (op_papply):
        case (op_apply):
        case (op_tail_apply):
        case (op_compose):
        case (op_lookup):
            Error("unimplemented");
            break;
        default:
            Error("unrecognized opcode");
        }
    }
};

#endif
